﻿#region File and License Information
/*
<File>
	<License>
		Copyright © 2009 - 2017, Daniel Vaughan. All rights reserved.
		This file is part of Calcium (http://calciumsdk.net), 
		which is released under the MIT License.
		See file /Documentation/License.txt for details.
	</License>
	<CreationDate>2010-10-21 15:34:42Z</CreationDate>
</File>
*/
#endregion

using System.Windows.Input;
using Outcoder.CommandModel;

#if NETFX_CORE
using Windows.UI.Xaml;
#else
using System;
using System.Reflection;
using System.Windows;
using System.Windows.Data;
#endif

namespace Outcoder.UI.Xaml
{
	/* TODO: [DV] Comment. */
	public static class Commanding
	{
		#region Command Property

		public static DependencyProperty CommandProperty
			= DependencyProperty.RegisterAttached(
				"Command",
				typeof(ICommand),
				typeof(Commanding),
#if NETFX_CORE
				new PropertyMetadata(null, HandleCommandChanged));
#else
				new PropertyMetadata(HandleCommandChanged));
#endif

		public static void SetCommand(DependencyObject obj, ICommand propertyValue)
		{
			obj.SetValue(CommandProperty, propertyValue);
		}

		public static ICommand GetCommand(DependencyObject obj)
		{
			return (ICommand)obj.GetValue(CommandProperty);
		}

#if WINDOWS_PHONE
		const string defaultEventName = "Tap";
#else
		const string defaultEventName = "Click";
#endif
		static void HandleCommandChanged(
			DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			CommandAttachment attachment = GetBehavior(d);
			var command = e.NewValue as ICommand;
			attachment.Command = command;
			if (command != null && string.IsNullOrEmpty(attachment.EventName))
			{
				var eventCommand = e.NewValue as IEventCommand;
				string eventName = eventCommand != null
							&& !string.IsNullOrEmpty(eventCommand.EventName)
								? eventCommand.EventName : defaultEventName;
				attachment.EventName = eventName;
			}

			var element = d as FrameworkElement;
			if (element != null)
			{
				element.Unloaded -= HandleAssociatedObjectUnloaded;
				element.Unloaded += HandleAssociatedObjectUnloaded;
				element.Loaded -= HandleAssociatedObjectLoaded;
				element.Loaded += HandleAssociatedObjectLoaded;
			}
		}

		static void HandleAssociatedObjectUnloaded(object sender, RoutedEventArgs args)
		{
			CommandAttachment attachment = GetBehavior((DependencyObject)sender);
			Detach(attachment);
		}

		static void Detach(CommandAttachment attachment)
		{
			attachment.Detach();
		}

		static void HandleAssociatedObjectLoaded(object sender, RoutedEventArgs e)
		{
			var d = (DependencyObject)sender;
			CommandAttachment attachment = GetBehavior(d);
			Attach(d, attachment);
		}

		static void Attach(DependencyObject d, CommandAttachment attachment)
		{
			attachment.Detach();
			attachment.Attach(d);
			attachment.Command = GetCommand(d);
			string eventName = GetEvent(d);
			if (string.IsNullOrWhiteSpace(eventName))
			{
				eventName = defaultEventName;
			}
			attachment.EventName = eventName;
			attachment.CommandParameter = GetCommandParameter(d);
		}

		#endregion

		#region Command Parameter

		public static readonly DependencyProperty CommandParameterProperty
			= DependencyProperty.RegisterAttached(
				"CommandParameter",
				typeof(object),
				typeof(Commanding),
#if NETFX_CORE
				new PropertyMetadata(null, HandleCommandParameterChanged));
#else
				new PropertyMetadata(HandleCommandParameterChanged));
#endif
		static void HandleCommandParameterChanged(
			DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			CommandAttachment attachment = GetBehavior(d);
			attachment.CommandParameter = e.NewValue;
		}

		public static void SetCommandParameter(
			DependencyObject obj, object propertyValue)
		{
			obj.SetValue(CommandParameterProperty, propertyValue);
		}

		public static object GetCommandParameter(DependencyObject obj)
		{
			return obj.GetValue(CommandParameterProperty);
		}

		#endregion

		#region Event Property

		public static DependencyProperty EventProperty
			= DependencyProperty.RegisterAttached(
				"Event",
				typeof(string),
				typeof(Commanding),
				#if NETFX_CORE
				new PropertyMetadata(null, HandleEventChanged));
#else
				new PropertyMetadata(HandleEventChanged));
#endif

		public static void SetEvent(DependencyObject obj, string propertyValue)
		{
			obj.SetValue(EventProperty, propertyValue);
		}

		public static string GetEvent(DependencyObject obj)
		{
			return (string)obj.GetValue(EventProperty);
		}

		static void HandleEventChanged(
			DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			CommandAttachment attachment = GetBehavior(d);
			var eventName = e.NewValue as string;
			attachment.EventName = eventName;
		}

		#endregion

		#region CommandBehavior Property

		static readonly DependencyProperty CommandBehaviorProperty
			= DependencyProperty.RegisterAttached(
				"CommandBehavior",
				typeof(CommandAttachment),
				typeof(Commanding),
				null);

		static CommandAttachment GetBehavior(DependencyObject d)
		{
			var attachment = (CommandAttachment)d.GetValue(CommandBehaviorProperty);

			if (attachment == null)
			{
				attachment = new CommandAttachment(d);
				d.SetValue(CommandBehaviorProperty, attachment);
			}

			return attachment;
		}

		#endregion

// Experimental
//#if SILVERLIGHT
//		#region CommandPath Property
//
//		public static DependencyProperty CommandPathProperty
//			= DependencyProperty.RegisterAttached(
//				"CommandPath",
//				typeof(string),
//				typeof(Commanding),
//				new PropertyMetadata(HandleCommandPathChanged));
//
//		public static void SetCommandPath(DependencyObject obj, string propertyValue)
//		{
//			obj.SetValue(CommandPathProperty, propertyValue);
//		}
//
//		public static string GetCommandPath(DependencyObject obj)
//		{
//			return (string)obj.GetValue(CommandPathProperty);
//		}
//
//		static void HandleCommandPathChanged(
//			DependencyObject d, DependencyPropertyChangedEventArgs e)
//		{
//			UIElement element = d as UIElement;
//			if (element == null)
//			{
//				return;
//			}
//			string commandName = e.NewValue != null ? e.NewValue.ToString() : null;
//			FrameworkElement ancestorElement = VisualTree.GetAncestorOrSelf<FrameworkElement>(element);
//			ResolveAndSetCommand(d, ancestorElement, commandName);
//		}
//
//		#endregion
//
//		static void ResolveAndSetCommand(
//			DependencyObject target, FrameworkElement source, string commandName)
//		{
//			object dataContext = source.DataContext;
//			if (dataContext == null)
//			{
//				/* In SL5 maybe handle data context changed. */
//				return;
//			}
//
//			if (string.IsNullOrWhiteSpace(commandName))
//			{
//				target.ClearValue(CommandProperty);
//			}
//
//			PropertyInfo propertyInfo = dataContext.GetType().GetProperty(commandName);
//			if (propertyInfo == null)
//			{
//				return;
//			}
//			object propertyValue = propertyInfo.GetValue(dataContext, new object[] { });
//			ICommand command = propertyValue as ICommand;
//			if (command == null)
//			{
//				target.ClearValue(CommandProperty);
//				return;
//			}
//
//			target.SetValue(CommandProperty, command);
//		}
//		#region DataContextProvider Property
//
//		public static DependencyProperty DataContextProviderProperty
//			= DependencyProperty.RegisterAttached(
//				"DataContextProvider",
//				typeof(FrameworkElement),
//				typeof(Commanding),
//				new PropertyMetadata(HandleDataContextProviderChanged));
//
//		public static void SetDataContextProvider(DependencyObject obj, FrameworkElement propertyValue)
//		{
//			obj.SetValue(DataContextProviderProperty, propertyValue);
//		}
//
//		public static FrameworkElement GetDataContextProvider(DependencyObject obj)
//		{
//			return (FrameworkElement)obj.GetValue(DataContextProviderProperty);
//		}
//
//		static void HandleDataContextProviderChanged(
//			DependencyObject d, DependencyPropertyChangedEventArgs e)
//		{
//			FrameworkElement element = e.NewValue as FrameworkElement;
//			if (element == null)
//			{
//				return;
//			}
//
//			d.Dispatcher.BeginInvoke(
//				delegate
//			        {
//			        	string commandName = GetCommandPath(d);
//						if (commandName == null)
//						{
//							return;
//						}
//						ResolveAndSetCommand(d, element, commandName);                       		
//			        });
//		}
//
//		#endregion
//#endif
	}
}